// 1. 使用SetNonBlocking：保证任何套接字操作都不会阻塞事件循环，避免单个连接阻塞整个服务器。
// 2. epoll_wait事件驱动：让线程挂起于内核事件队列，只有真正有事件时才被唤醒，防止忙轮询。
// 3. 循环 accept/recv：非阻塞套接字返回 EAGAIN代表暂时无更多数据，通过循环读取直到 EAGAIN 以清空内核缓冲。
// 4. 资源管理：当检测到 EPOLLERR/EPOLLHUP 或 recv 错误时关闭 fd，防止泄漏。
// 5. 优缺点体现：一个线程即可管理大量连接（优点），但需要编写事件循环和状态机（缺点）。

#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cerrno>
#include <cstring>
#include <iostream>
#include <stdexcept>

constexpr int kListenPort = 8080;
constexpr int kBacklog    = 128;
constexpr int kMaxEvents  = 64;
constexpr size_t kBufSize = 4096;

/**
 * 将文件描述符设置为非阻塞模式。
 * 这么做的原因：
 *   1. 保证所有 I/O 调用立即返回，不会让事件循环卡在单个连接上。
 *   2. 配合 epoll 使用时，可以一次处理多个就绪事件，提升吞吐量。
 *   3. 避免线程被阻塞，从而减少线程数量，实现一个线程处理大量连接。
 */
void SetNonBlocking(int fd) {
    int flags = ::fcntl(fd, F_GETFL, 0);
    if (flags == -1) {
        throw std::runtime_error("fcntl(F_GETFL) failed");
    }
    if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
        throw std::runtime_error("fcntl(F_SETFL) failed");
    }
}

/**
 * 当 epoll 通知某个连接可读时，执行非阻塞 recv。
 * 需要循环调用 recv，直到返回 EAGAIN，避免漏读数据。
 * 原因说明：
 *   - 在 ET（边缘触发）模式下，如果不读空缓冲区，会错过后续数据。
 *   - 在 LT（水平触发）模式下，读到 EAGAIN 也是良好实践，可减少无谓通知。
 */
void HandleReadable(int conn_fd) {
    char buf[kBufSize];

    while (true) {
        ssize_t n = ::recv(conn_fd, buf, sizeof(buf), 0);

        if (n > 0) {
            // 非阻塞 recv 成功，立即处理数据。
            std::string payload(buf, buf + n);
            std::cout << "[fd " << conn_fd << "] received: " << payload;
            // 示例：简单回显。真实业务可放入任务队列或线程池。
            ::send(conn_fd, payload.data(), payload.size(), 0);
        } else if (n == 0) {
            // 对端关闭连接，退出循环并关闭 fd。
            std::cout << "[fd " << conn_fd << "] peer closed connection\n";
            ::close(conn_fd);
            break;
        } else {
            // n == -1，检查 errno。
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                // 非阻塞语义：当前没有更多数据，等待下一次 epoll 通知。
                break;
            }
            if (errno == EINTR) {
                // 被信号打断，继续循环即可。
                continue;
            }
            std::cerr << "[fd " << conn_fd << "] recv error: "
                      << std::strerror(errno) << std::endl;
            ::close(conn_fd);
            break;
        }
    }
}

int main() {
    int listen_fd = ::socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd < 0) {
        throw std::runtime_error("socket() failed");
    }

    // 设置地址重用，便于服务器重启。
    int opt = 1;
    ::setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    sockaddr_in addr{};
    addr.sin_family      = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port        = htons(kListenPort);

    if (::bind(listen_fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
        ::close(listen_fd);
        throw std::runtime_error("bind() failed");
    }

    if (::listen(listen_fd, kBacklog) < 0) {
        ::close(listen_fd);
        throw std::runtime_error("listen() failed");
    }

    /**
     * 监听套接字也需要设为非阻塞：
     *   - 避免在 accept 上阻塞，确保事件循环持续运转。
     *   - 与 epoll 组合使用时，可以在一次回调中批量 accept 所有已到达的连接。
     */
    SetNonBlocking(listen_fd);

    int epoll_fd = ::epoll_create1(0);
    if (epoll_fd < 0) {
        ::close(listen_fd);
        throw std::runtime_error("epoll_create1() failed");
    }

    epoll_event ev{};
    ev.events  = EPOLLIN;   // 监听可读事件（新连接到达）。
    ev.data.fd = listen_fd;
    if (::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev) < 0) {
        ::close(epoll_fd);
        ::close(listen_fd);
        throw std::runtime_error("epoll_ctl(ADD listen_fd) failed");
    }

    std::cout << "Non-blocking epoll server listening on port "
              << kListenPort << std::endl;

    epoll_event events[kMaxEvents];

    while (true) {
        /**
         * epoll_wait 负责在内核态等待“就绪事件”。
         * 由于所有描述符都是非阻塞的，
         * 只有当发生状态变化时（有数据可读/可写），才会返回。
         * 这样可以避免忙轮询，大幅降低 CPU 占用。
         */
        int ready = ::epoll_wait(epoll_fd, events, kMaxEvents, -1);
        if (ready < 0) {
            if (errno == EINTR) {
                continue; // 被信号中断，重新等待。
            }
            std::cerr << "epoll_wait error: " << std::strerror(errno) << std::endl;
            break;
        }

        for (int i = 0; i < ready; ++i) {
            int fd  = events[i].data.fd;
            auto ev = events[i].events;

            if (fd == listen_fd) {
                /**
                 * 监听套接字就绪，表示有一个或多个新连接到达。
                 * 由于监听 fd 是非阻塞的，需要使用循环 accept，
                 * 直到返回 EAGAIN，确保一次把内核队列中的连接都取光。
                 */
                while (true) {
                    sockaddr_in client_addr{};
                    socklen_t   client_len = sizeof(client_addr);
                    int conn_fd = ::accept(listen_fd,
                                           reinterpret_cast<sockaddr*>(&client_addr),
                                           &client_len);
                    if (conn_fd < 0) {
                        if (errno == EAGAIN || errno == EWOULDBLOCK) {
                            // 所有待处理的连接已经取完。
                            break;
                        }
                        if (errno == EINTR) {
                            continue; // 被信号中断，重试 accept。
                        }
                        std::cerr << "accept error: "
                                  << std::strerror(errno) << std::endl;
                        break;
                    }

                    // 新连接同样设置为非阻塞，确保整个连接生命周期都遵循事件驱动模式。
                    SetNonBlocking(conn_fd);

                    epoll_event client_ev{};
                    client_ev.events  = EPOLLIN | EPOLLET; // 可读 + 边缘触发演示
                    client_ev.data.fd = conn_fd;
                    if (::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &client_ev) < 0) {
                        std::cerr << "epoll_ctl(ADD conn_fd) failed: "
                                  << std::strerror(errno) << std::endl;
                        ::close(conn_fd);
                        continue;
                    }

                    char ip[INET_ADDRSTRLEN];
                    ::inet_ntop(AF_INET, &client_addr.sin_addr, ip, sizeof(ip));
                    std::cout << "[fd " << conn_fd << "] connected from "
                              << ip << ":" << ntohs(client_addr.sin_port) << std::endl;
                }
            } else if (ev & (EPOLLERR | EPOLLHUP)) {
                // 异常或挂起事件：直接关闭连接，避免占用资源。
                std::cerr << "[fd " << fd << "] epoll err/hup\n";
                ::close(fd);
            } else if (ev & EPOLLIN) {
                // 普通读事件，调用非阻塞读处理函数。
                HandleReadable(fd);
            }
            // 写事件（EPOLLOUT）可选，根据业务需求添加。
        }
    }

    ::close(epoll_fd);
    ::close(listen_fd);
    return 0;
}